﻿using System;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
using NCMIS.ObjectModel.MetaData;

namespace NCMIS.ObjectModel.Rest
{
    /// <summary>
    /// Represents the content element (cmisra:content) of a CMIS extended Atom entry. This element takes
    /// precedence over the atom:content element when sent from a client. When returning a content response
    /// to the client a repository must not use this element, but the atom:content element.
    /// </summary>
    [DataContract(Name = "content", Namespace = CmisNs.Cmisra)]
    public class Content
    {
        /// <summary>
        /// The media type of the content (RFC4288).
        /// </summary>
        [DataMember(Name = "mediatype")]
        public string MediaType
        {
            get;
            set;
        }

        /// <summary>
        /// The base64 encoded byte content of the file.
        /// </summary>
        [DataMember(Name = "base64")]
        public string Base64
        {
            get;
            set;
        }
    }

    /// <summary>
    /// Supports serializing using streamed content rather than "in-memory" (Base64 string in Content above)
    /// </summary>
    public class CmisRaContentStream : Content
    {
        public Stream Stream;

        public CmisRaContentStream(Content c, Stream contentStream)
        {
            this.MediaType = c.MediaType;
            this.Base64 = c.Base64;

            Stream = contentStream;
        }
    }

    /// <summary>
    /// Serializes the ContentStream above by reading from contentStream
    /// </summary>
    public class CmisRaContentStreamSerializer : XmlObjectSerializer
    {
        // Override WriteObjectContent to control how <cmisra:Content> is serialized.
        // We want to read from a stream and produce the Base64-encoded string "on the go"
        // without the need to (temporarily) store the whole thing in memory
        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            /*
                We need to produce the following output:
              
                <cmisra:base64>UEsDBBQABgAIAAAAIQDd/JU3ZgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiB</cmisra:base64> 
                <cmisra:mediatype>application/vnd.openxmlformats-officedocument.wordprocessingml.document</cmisra:mediatype> 
            */
            writer.WriteStartElement("base64", CmisNs.Cmisra);

            CmisRaContentStream cs = graph as CmisRaContentStream;

            if(null!=cs)
            {
                // read from contentstream and write as Base64-encoded string to writer
                //
                // NOTE: Make sure that buffer-length is evenly divided by 3 as intermediate padding of the Base64-encoded chunks
                // might otherwise distort the "complete" base64-encoded string
                // (If each chunk is evenly divided by 3, no padding is needed, and all bas64-encoded chunks can simple be
                // concatenated together)
                byte[] buffer = new byte[900];
                int read = 0;
                while(0 < (read = cs.Stream.Read(buffer, 0, buffer.Length)))
                {
                    writer.WriteBase64(buffer, 0, read);
                }
            }
            writer.WriteEndElement();
            writer.WriteElementString("mediatype", CmisNs.Cmisra, cs.MediaType);
        }

        public override bool IsStartObject(System.Xml.XmlDictionaryReader reader)
        {
            throw new NotImplementedException();
        }

        public override object ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName)
        {
            throw new NotImplementedException();
        }

        public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer)
        {
            throw new NotImplementedException();
        }

        public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }
    }
}
